package com.leyou.order.service;

import com.leyou.common.auth.entity.UserInfo;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.utils.IdWorker;
import com.leyou.dto.AddressDTO;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.order.dto.CartDTO;
import com.leyou.order.dto.OrderDTO;
import com.leyou.order.entity.Order;
import com.leyou.order.entity.OrderDetail;
import com.leyou.order.entity.OrderLogistics;
import com.leyou.order.enums.OrderStatusEnum;
import com.leyou.order.interceptors.LoginInterceptor;
import com.leyou.order.mapper.OrderDetailMapper;
import com.leyou.order.mapper.OrderLogisticsMapper;
import com.leyou.order.mapper.OrderMapper;
import com.leyou.order.utils.PayHelper;
import com.leyou.user.client.UserClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderService {

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private UserClient userClient;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderLogisticsMapper orderLogisticsMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private PayHelper payHelper;


    @Transactional
    public Long createOrder(OrderDTO orderDTO) {

        long orderId = idWorker.nextId();
        UserInfo userInfo = LoginInterceptor.getUserInfo();

        //加工订单详情

        List<CartDTO> carts = orderDTO.getCarts();
        List<Long> skuIds = carts.stream().map(CartDTO::getSkuId).collect(Collectors.toList());
        Map<Long,Integer> cartsMap=carts.stream().collect(Collectors.toMap(CartDTO::getSkuId,CartDTO::getNum));
        List<SkuDTO> skuDTOS = itemClient.querySkuByIds(skuIds);
        List<OrderDetail> orderDetails=new ArrayList<>();
        long totalFee=0;

        for (SkuDTO skuDTO :skuDTOS) {

            OrderDetail orderDetail= BeanHelper.copyProperties(skuDTO,OrderDetail.class);
            orderDetail.setId(null);
            orderDetail.setSkuId(skuDTO.getId());
            orderDetail.setOrderId(orderId);
            orderDetail.setImage(StringUtils.substringBefore(skuDTO.getImages(),","));
            Integer num=cartsMap.get(skuDTO.getId());
            orderDetail.setNum(num);
            totalFee+=skuDTO.getPrice()*num;
            orderDetails.add(orderDetail);

        }

        //加工订单

        Order order = BeanHelper.copyProperties(orderDTO, Order.class);
        order.setOrderId(orderId);
        order.setTotalFee(totalFee);
        order.setActualFee(totalFee);
        order.setPostFee(0L);

        order.setUserId(userInfo.getId());

        //设置订单状态
        order.setStatus(OrderStatusEnum.INIT.value());

        //获取订单地址
        AddressDTO addressDTO = userClient.queryAddressById(orderDTO.getAddressId());
        OrderLogistics orderLogistics = BeanHelper.copyProperties(addressDTO, OrderLogistics.class);
        orderLogistics.setOrderId(orderId);

        //一切就绪保存到数据库

        //保存order订单
        int count=orderMapper.insertSelective(order);
        if (1!=count){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        //保存OrderDetail订单详情
        count = orderDetailMapper.insertList(orderDetails);
        if (count!=orderDetails.size()){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

        //保存物流信息
        count=orderLogisticsMapper.insertSelective(orderLogistics);
        if (1!=count){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }


        //减库存

        itemClient.minusStock(cartsMap);


        return orderId;
    }


    public Order queryOrderById(Long orderId) {

        Order order = orderMapper.selectByPrimaryKey(orderId);
        if (null==order){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }

        return order;

    }


    public String getPayUrl(Long orderId) {

        String key="ly.pay.url."+orderId;
        String cacheUrl = redisTemplate.opsForValue().get(key);
        if (cacheUrl!=null){
            return cacheUrl;
        }
        Order order = orderMapper.selectByPrimaryKey(orderId);
        if (null==order){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        String url = payHelper.createOrder(orderId, 1L, "哈哈哈哈");
        // 存入redis，设置有效期为 2小时
        redisTemplate.opsForValue().set(key, url, 2, TimeUnit.HOURS);

        return url;
    }


    @Transactional
    public void handleNotify(Map<String, String> result) {
        // 1 签名校验
        try {
            payHelper.isValidSign(result);
        }catch (Exception e){
            log.error("【微信回调】微信签名有误！, result: {}",result, e);
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_SIGN, e);
        }
        // 2、业务校验
        payHelper.checkResultCode(result);

        // 3 校验金额数据
        String totalFeeStr = result.get("total_fee");
        String tradeNo = result.get("out_trade_no");
        if(StringUtils.isEmpty(totalFeeStr) || StringUtils.isEmpty(tradeNo)){
            // 回调参数中必须包含订单编号和订单金额
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }
        // 3.1 获取结果中的金额
        long totalFee = Long.valueOf(totalFeeStr);
        // 3.2 获取订单
        Long orderId = Long.valueOf(tradeNo);
        Order order = orderMapper.selectByPrimaryKey(orderId);
        // 3.3.判断订单的状态，保证幂等
        if(!order.getStatus().equals(OrderStatusEnum.INIT.value())){
            // 订单已经支付，返回成功
            return;
        }
        // 3.4.判断金额是否一致
        if(totalFee != /*order.getActualPay()*/ 1){
            // 金额不符
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }

        // 4 修改订单状态
        Order orderStatus = new Order();
        orderStatus.setStatus(OrderStatusEnum.PAY_UP.value());
        orderStatus.setOrderId(orderId);
        orderStatus.setPayTime(new Date());
        int count = orderMapper.updateByPrimaryKeySelective(orderStatus);
        if(count != 1){
            log.error("【微信回调】更新订单状态失败，订单id：{}", orderId);
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }
        log.info("【微信回调】, 订单支付成功! 订单编号:{}", orderId);
    }


    public Integer queryPayStatus(Long orderId) {
        Order order = orderMapper.selectByPrimaryKey(orderId);
        if(order == null){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        return order.getStatus();
    }

    public List<Order> queryOrderByStatus(Integer state) {

        Order record=new Order();
        record.setStatus(state);
        List<Order> select = orderMapper.select(record);
        if (CollectionUtils.isEmpty(select)){
            log.info("【根据状态查询订单】未查询到订单");
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        log.info("【根据状态查询订单】查询到订单");
        return select;
    }

    public void updateStatus(List<Order> outTimeOrders) {

        outTimeOrders.forEach(order -> {
            order.setStatus(OrderStatusEnum.CLOSED.value());
            orderMapper.updateByPrimaryKeySelective(order);
        });

    }

    public List<OrderDetail> queryOrderDetailByOrderId(List<Long> orderIds) {

        List<OrderDetail> orderDetails=new ArrayList<>();
        orderIds.forEach(orderId->{
            OrderDetail record=new OrderDetail();
            record.setOrderId(orderId);
            orderDetails.addAll(orderDetailMapper.select(record));

        });

        return orderDetails;
    }
}
